#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"
#include "Common.hpp"

using namespace LogModule;

const std::string dev = "/dev/null";

// 将服务进行守护进程化的方法
void Daemon(int nochdir, int noclose)
{
    // 1. 我们的守护进程得忽略IO、子进程退出等相关的信号
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    // 2. 我们让父进程直接结束来避免setsid出现错误
    // 来符合其调用进程不是进程组组长的要求
    if (fork() > 0)
    {
        exit(0);
    }

    // 3. 到这就只有子进程了,子进程成为了孤儿进程，其父进程就是1
    setsid(); // 让当前子进程成为一个独立的会话

    if (nochdir == 0)
    {
        // 更改当前进程的工作路径到根目录下
        chdir("/");
    }

    // 4. 依旧可能显示器、键盘、stdin、stdout、stderr关联的
    // 守护进程不从键盘输入，也不需要向显示器打印
    // 网络服务数据只会从网卡中读取，从套接字中读，往套接字中写，IO是套接字（标准输出、输入）
    // 方法1：直接关闭标准输入、输出和错误的描述符0、1、2 ————不推荐
    // 方法2：打开/dev/null文件，重定向标准输入、输出、错误到/dev/null中
    //       因为凡是写入这个文件的内容会被丢弃，凡是从这个文件中读取就会读到空，没有影响
    if (noclose == 0)
    {
        int fd = ::open(dev.c_str(), O_RDWR); // 以读写方式打开
        if (fd < 0)
        {
            // 打开/dev/null文件失败
            LOG(LogLevel::FATAL) << "open " << dev << " error";
            exit(OPEN_ERR);
        }
        else
        {
            // 打开/dev/null文件成功
            // 将我们的标准输入、输出、错误重定向到该文件中去，使用dup2函数
            dup2(fd, 0); // 标准输入重定向
            dup2(fd, 1); // 标准输出重定向
            dup2(fd, 2); // 标准错误重定向

            close(fd);
        }
    }
}